#include "Semantic.h"
#include "TreeNode.h"
#include "HashTable.h"
#include <string.h>

int semantic_error=0;

bool typeEqual(Type lhs, Type rhs){
	assert(lhs!=NULL && rhs!=NULL);
		
	if(lhs->kind != rhs->kind)
		return FALSE;

	if(lhs->kind == BASIC){
		if(lhs->basic == rhs->basic)
			return TRUE;
		else
			return FALSE;
	}
	else if(lhs->kind == ARRAY){
		//recursively guaranteed base and dimension are the same
		return typeEqual(lhs->array.elem, rhs->array.elem);
	}
	else if(lhs->kind == STRUCTURE){
		return structEqual(lhs->structure, rhs->structure);
	}
	else if(lhs->kind == FUNCTION){
		// It doesn't need to judge whether functions equal.
		assert(0);
	}
	
	return 0;
}

bool structEqual(Structure lhs, Structure rhs){
	//adopt Name Equivalence
	if(strcmp(lhs->name,rhs->name)==0)
		return TRUE;
	return FALSE;
}

/* High-level Definitions */
void Program(TreeNode* root){
	//empty program
	if(root==0)
		return;
	// ExtDefList
	ExtDefList(root->des);
}

void ExtDefList(TreeNode* n){
	// ExtDefList -> NULL
	if(n==NULL)
		return;

	// ExtDef ExtDefList
	assert(n->des);
	ExtDef(n->des);
	ExtDefList(n->des->sib);
}

/* Global Variable */
void ExtDef(TreeNode *n){
	assert(n);

	TreeNode* des = n->des;
	Type type;
	type = Specifier(des);
	des = des->sib;

	// Specifier SEMI
	if(strcmp(des->name,"SEMI")==0)
		return;
	// Specifier ExtDecList SEMI
	else if(strcmp(des->name,"ExtDecList")==0)
		ExtDecList(des,type);
	// Specifier FunDec CompSt|SEMI
	else if(strcmp(des->name,"FunDec")==0){
		Function func=FunDec(des,type);
	
		des = des->sib;
		int i;
		// Specifier FunDec CompSt
		if(strcmp(des->name,"CompSt")==0){
			func->isDefined = 1;
			int flag=funcChk_Inst(func);
			if(flag==FALSE)
			{
				printf("Error type 4 at Line %d: Redefined function \"%s\"\n",func->line,func->name);
				semantic_error++;
			}
			top=1;//func field

			for(FieldList p=func->param;p;p=p->tail)
			{
				int flag = varInsertCheck(p);
				if(flag==FALSE)
				{
					printf("Error type 3 at Line %d: Redefined variable \"%s\".\n",func->line, p->name);
					semantic_error++;
					continue;
				}
				else varInsertTable(p);		
			}
			top--;//top=0 to be ++
			CompSt(des,func);//didn't determine interface beforehand can just use return type
		}
		// Specifier FunDec SEMI
		else if(strcmp(des->name,"SEMI")==0){
			//do not support function declaration
			printf("Error type B at Line %d: Incomplete definition of function \"%s\"\n",func->line,func->name);
			semantic_error++;
		}
		else
			assert(0);
	}
	else
		assert(0);
}

void ExtDecList(TreeNode *n, Type type){
	assert(n);
	
	TreeNode *des = n->des;
	assert(des);
	VarDec(des, type, FROM_VARIABLE);
	
	des = des->sib;
	//->VarDec
	if(des == NULL)
		return;

	// VarDec COMMA ExtDecList
	des = des->sib;
	ExtDecList(des, type);
}

/* Specifiers */
Type Specifier(TreeNode *n){
	assert(n);	
	TreeNode *des = n->des;
	assert(des);

	Type type;

	// TYPE
	if(strcmp(des->name,"TYPE")==0){
		type = (Type)malloc(sizeof(struct Type_));
		type->kind = BASIC;
		if(strcmp(des->attr_val,"int")==0){
			type->basic = TYPE_INT;
		}
		else if(strcmp(des->attr_val, "float")==0){
			type->basic = TYPE_FLOAT;
		}
		else
			assert(0);
		return type;
	}
	// StructSpecifier
	else if(strcmp(des->name, "StructSpecifier")==0){
		type = StructSpecifier(des);
		return type;
	}
	else
		assert(0);
	
}

Type StructSpecifier(TreeNode *n){
	assert(n);
	
	TreeNode *des = n->des;
	assert(des);
	assert(strcmp(des->name,"STRUCT")==0);

	des = des->sib;

	// STRUCT Tag    to reference
	if(strcmp(des->name, "Tag")==0){
		Type result = getTable(des->des->attr_val);		
		if(result==NULL || result->kind!=STRUCTURE || 
						strcmp(result->structure->name,des->des->attr_val)!=0)
		{
			printf("Error type 17 at Line %d: Undefined structure \"%s\".\n",n->des->row, des->des->attr_val);
			semantic_error++;
			return NULL;
		}
		return result;
	}
	
	// STRUCT (OptTag) LC (DefList) RC     to define
	Type type = (Type)malloc(sizeof(struct Type_));
	type->kind = STRUCTURE;
	Structure structure = (Structure)malloc(sizeof(struct Structure_));
	type->structure = structure;

	if(strcmp(des->name, "OptTag")==0)
	{
		// OptTag -> ID
		assert(strcmp(des->des->name, "ID")==0);
		structure->name = des->des->attr_val;
		des=des->sib;
		assert(strcmp(des->name, "LC")==0);
	}
	else if(strcmp(des->name, "LC")==0)	
		// OptTag -> NULL
		structure->name = NULL;
	else
		assert(0);

	des = des->sib;
	if(strcmp(des->name, "DefList")==0)
	{
		StkPush();
		structure->domain = DefList(des,FROM_FIELD);
		StkPop();
	}
	else 
		assert(strcmp(des->name, "RC")==0);

	//name == NULL  then does not have to insert such a structure
	if(structure->name!=NULL){
		int flag = structInsertCheck(structure);
		if(flag == FALSE){
			printf("Error type 16 at Line %d: Duplicated name \"%s\".\n",n->des->row, structure->name);
			semantic_error++;
			return NULL;
		}
		structInsertTable(structure);				
	}

	return type;
	
}

/* Declarators */
FieldList VarDec(TreeNode *n, Type type, From from){
	assert(n);
	
	TreeNode *des = n->des;
	assert(des);

	// ID
	if(strcmp(des->name, "ID")==0){
		FieldList varDec = (FieldList)malloc(sizeof(struct FieldList_));
		varDec->name = des->attr_val;
		varDec->type = type;
		varDec->tail = NULL;

		if(from==FROM_PARAM)
			return varDec;

		int flag = varInsertCheck(varDec);
		if(flag==FALSE){
			if(from==FROM_VARIABLE){
				printf("Error type 3 at Line %d: Redefined variable \"%s\".\n",des->row, varDec->name);
				semantic_error++;
			}
			else{
				printf("Error type 15 at Line %d: Redefined field \"%s\".\n",des->row, varDec->name);
				semantic_error++;
			}
			return NULL;
		}
		
		varInsertTable(varDec);
		return varDec;
	}
	// VarDec LB INT RB
	else if(strcmp(des->name, "VarDec")==0){

		Type arr = (Type)malloc(sizeof(struct Type_));
		arr->kind = ARRAY;
		arr->array.size = (int)strtol(des->sib->sib->attr_val,NULL,10);
		arr->array.elem = type; 

		return VarDec(des, arr, from);
	}
	else
		assert(0);
}

Function FunDec(TreeNode *n, Type type){
	assert(n);
	
	TreeNode *des = n->des;
	Function func = (Function)malloc(sizeof(struct Function_));
	assert(func);
	func->name = des->attr_val;
	func->isDefined = 0;
	func->param = NULL;
	func->r_type = type;
	func->line = des->row;

	des = des->sib->sib;
	// ID LP VarList RP
	if(strcmp(des->name,"VarList")==0){
		func->param = VarList(des);
	}
	return func;
}

FieldList VarList(TreeNode *n){
	assert(n);
	
	TreeNode *des = n->des;
	// ParamDec
	FieldList varList = ParamDec(des);
	des = des->sib;
	
	// ParamDec COMMA VarList 
	if(des != NULL){
		des = des->sib;
		varList->tail = VarList(des);
	}
	
	return varList;
}

FieldList ParamDec(TreeNode *n){
	if(n==NULL)
		return NULL;
	//printf("%s\n",n->name);
	
	TreeNode *des = n->des;
	// Specifier VarDec
	Type type = Specifier(des);
	des = des->sib;
	if(type==NULL)
		return NULL;
	FieldList paramDec = VarDec(des, type, FROM_PARAM);
	return paramDec;
}

/* Statements */
void CompSt(TreeNode *n, Function BlngFunc){
	assert(n);

	StkPush();
	
	TreeNode *des = n->des;
	// LC (DefList) (StmtList) RC
	des = des->sib;
	if(strcmp(des->name,"DefList")==0)
	{
		DefList(des, FROM_VARIABLE);
		des = des->sib;
	}
	if(strcmp(des->name,"StmtList")==0)
		StmtList(des, BlngFunc);

	StkPop();
}

void StmtList(TreeNode *n, Function BlngFunc){
	// StmtList -> NULL
	if(n==NULL)
		return;

	TreeNode *des = n->des;
	assert(des);
	assert(strcmp(des->name,"Stmt")==0);
	
	// Stmt StmtList
	Stmt(des, BlngFunc);
	StmtList(des->sib, BlngFunc);
}

void Stmt(TreeNode *n, Function BlngFunc){
	assert(n);
	
	TreeNode *des = n->des;
	// Exp SEMI
	if(strcmp(des->name, "Exp")==0){
		Exp(des);
	}
	// CompSt
	else if(strcmp(des->name, "CompSt")==0){
		CompSt(des, BlngFunc);
	}
	// RETURN Exp SEMI
	else if(strcmp(des->name, "RETURN")==0){
		Type expType=Exp(des->sib);
		if(expType==NULL||BlngFunc->r_type==NULL) return;
		if(typeEqual(BlngFunc->r_type,expType)!=0){
			printf("Error type 8 at Line %d: Type mismatched for return.\n",des->row);
			semantic_error++;
		}
	}
	// IF LP Exp Stmt (ELSE Stmt)?
	else if(strcmp(des->name, "IF")==0){
		des = des->sib->sib;
		Type expType=Exp(des);
		des = des->sib->sib;
		Stmt(des, BlngFunc);
		des = des->sib;
		if(des == NULL)
			return;
		des = des->sib;
		Stmt(des, BlngFunc);
	}
	// WHILE LP Exp RP Stmt
	else if(strcmp(des->name, "WHILE")==0){
		des = des->sib->sib;
		Exp(des);
		des = des->sib->sib;
		Stmt(des, BlngFunc);
	}
}

/* Local Definitions */
FieldList DefList(TreeNode *n, From from){
	// DefList -> NULL
	if(n==NULL)
		return NULL;
	
	TreeNode *des = n->des;
	assert(des);
	assert(strcmp(des->name,"Def")==0);

	// Def DefList 
	FieldList defList = Def(des, from);

	if(defList == NULL){//error virables, ignore
		defList = DefList(des->sib, from);
		return defList;
	}

	FieldList tmp = defList;
	while(tmp->tail != NULL)
		tmp = tmp->tail;
	tmp->tail = DefList(des->sib, from);
	return defList;
}

FieldList Def(TreeNode *n, From from){
	assert(n);
	assert(strcmp(n->name,"Def")==0);
	
	TreeNode *des = n->des;
	// Specifier DecList SEMI
	
	Type type = Specifier(des);
	des = des->sib;
	FieldList def = DecList(des, type, from);
	return def;
}

FieldList DecList(TreeNode *n, Type type, From from)
{
	assert(n);
	assert(strcmp(n->name,"DecList")==0);
	
	TreeNode *des = n->des;
	FieldList decList = NULL;

	decList = Dec(des, type, from);
	des = des->sib;

	// Dec
	if(des == NULL)
		return decList;
	// Dec COMMA DecList
	des = des->sib;
	if(decList==NULL){
		decList = DecList(des, type, from);
	}
	else{
		FieldList tmpDec = decList;
		while(tmpDec->tail != NULL)
				tmpDec = tmpDec->tail;
		tmpDec->tail = DecList(des, type, from);
	}
	return decList;
}

FieldList Dec(TreeNode *n, Type type, From from){
	assert(n);
	
	/*可以保证每次传给VarDec的type是唯一的  如此可以修复一些错误 但即使不free还是有段错误
	Type type_cp=(Type)malloc(sizeof(struct Type_));
	*type_cp=*type;
	// type_cp->assign=type->assign;
	// type_cp->u=type->u;
	// type_cp->kind=type->kind;

	还是会有些纳闷 VarDec还非要同一个type才行？ 以后有机会再debug
	*/

	TreeNode *des = n->des;
	FieldList dec = VarDec(des, type, from);

	des = des->sib;
	// VarDec
	if(des == NULL)
		return dec;
	// VarDec ASSIGNOP Exp
	if(from == FROM_FIELD){
		printf("Error type 15 at Line %d: initialization in field \"%s\"\n",des->row, dec->name);
		semantic_error++;
		return NULL;
	}
	des = des->sib;
	Type expType = Exp(des);
	if(typeEqual(type, expType)!=0){
		printf("Error type 5 at Line %d: The type mismatched\n",des->row);
		semantic_error++;
		return NULL;
	}
	free(expType);
	return dec;
}

/* Expressions */
Type Exp(TreeNode *n){
	assert(n);
	
	TreeNode *des = n->des;

	if(strcmp(des->name,"LP")==0){
		//Exp->LP Exp RP
		des = des->sib;
		return Exp(des);
	}
	
	if(strcmp(des->name,"MINUS")==0){
		//Exp->MINUS Exp
		des = des->sib;
		Type type = Exp(des);
		if(type==NULL)//error
			return NULL;
		if(type->kind!=BASIC){
			printf("Error type 7 at Line %d: Operands type mismatched\n", des->row);
			semantic_error++;
			free(type);
			return NULL;
		}

		type->assign = RIGHT;
		return type;
	}
	
	if(strcmp(des->name,"NOT")==0){
		//Exp->NOT Exp
		des = des->sib;
		Type type = Exp(des);
		if(type==NULL)
			return NULL;
		if(type->kind!=BASIC || type->basic!=TYPE_INT){
			printf("Error type 7 at Line %d: Operands type mismatched\n", des->row);
			semantic_error++;
			free(type);	
			return NULL;
		}

		type->assign = RIGHT;
		return type;
	}

	if(strcmp(des->name,"ID")==0){
		if(des->sib==NULL){
			//Exp->ID
			Type type = getTable(des->attr_val);  //判断是否定义过
			if(type==NULL||type->kind==FUNCTION){
				printf("Error type 1 at Line %d: Undefined variable \"%s\".\n", des->row, des->attr_val);	
				semantic_error++;
				return NULL;
			}
			Type rtn = (Type)malloc(sizeof(struct Type_));
			memcpy(rtn, type, sizeof(struct Type_));

			rtn->assign = BOTH;
			return rtn;
		}
		else{
			//Exp->ID LP RP | ID LP Args RP
			assert(strcmp(des->sib->name,"LP")==0);
			Type func = getTable(des->attr_val);   //判断是否定义过
			if(func==NULL){
				printf("Error type 2 at Line %d: Undefined function \"%s\".\n", des->row, des->attr_val);
				semantic_error++;
				return NULL;
			}
			if(func->kind!=FUNCTION){
				printf("Error type 11 at Line %d: \"%s\" is not a function.\n", des->row, des->attr_val);
				semantic_error++;
				return NULL;
			}
			FieldList param = func->function->param;
			des = des->sib->sib;
			if(strcmp(des->name,"RP")==0){
				if(param!=NULL){
					printf("Error type 9 at Line %d: Function \"%s\" is not applicable for arguments.\n", des->row, func->function->name);
					semantic_error++;
					return NULL;
				}
			}
			else{
				if(Args(des,param)!=0){  //比较两个类型是否匹配
					printf("Error type 9 at Line %d: Function \"%s\" is not applicable for arguments.\n", des->row, func->function->name);
					semantic_error++;
					return NULL;
				}
			}
			Type rtn = (Type)malloc(sizeof(struct Type_));
			memcpy(rtn, func->function->r_type, sizeof(struct Type_));

			rtn->assign = RIGHT;
			return rtn;
		}
	}
	else if(strcmp(des->name,"INT")==0){
		//Exp->INT
		Type type = (Type)malloc(sizeof(struct Type_));
		type->kind = BASIC;
		type->basic = TYPE_INT;
		type->assign = RIGHT;
		return type;
	}
	else if(strcmp(des->name,"FLOAT")==0){
		//Exp->FLOAT
		Type type = (Type)malloc(sizeof(struct Type_));
		type->kind = BASIC;
		type->basic = TYPE_FLOAT;
		type->assign = RIGHT;
		return type;
	}
	else if(strcmp(des->name,"Exp")==0){
		//Exp->Exp ...
		if(strcmp(des->sib->name,"ASSIGNOP")==0 ){
			//Exp->Exp ASSIGNOP Exp
			
			//判断表达式左边是否为左值
			Type lhs = Exp(des);
			Type rhs = Exp(des->sib->sib);
			if(lhs==NULL || rhs==NULL)	//error
				return NULL;
			if(lhs->assign==RIGHT){
				printf("Error type 6 at Line %d: The left-hand side of an assignment must be a variable.\n", des->row);
				semantic_error++;
				return NULL;
			}
			//判断赋值号两边表达式是否类型匹配
			if(typeEqual(lhs, rhs)==0){    
				return lhs;
			}
			else{
				printf("Error type 5 at Line %d: Type mismatched for assignment.\n", des->row);
				semantic_error++;
				return NULL;			
			}
		}
		if(strcmp(des->sib->name,"AND")==0 || 
						strcmp(des->sib->name,"OR")==0 || 
						strcmp(des->sib->name,"RELOP")==0 || 
						strcmp(des->sib->name,"PLUS")==0 || 
						strcmp(des->sib->name,"MINUS")==0 || 
						strcmp(des->sib->name,"STAR")==0 || 
						strcmp(des->sib->name,"DIV")==0){
			//Exp->Exp AND|OR|RELOP|PLUS|MINUS|STAR|DIV Exp
			Type lhs = Exp(des);
			Type rhs = Exp(des->sib->sib);
		
			if(lhs==NULL||rhs==NULL)
				return NULL;	
			if(lhs->kind==BASIC && rhs->kind==BASIC && 
							lhs->basic==rhs->basic){
				Type rtn = (Type)malloc(sizeof(struct Type_));
				memcpy(rtn, lhs, sizeof(struct Type_));
				rtn->assign = RIGHT;
				return rtn;
			}
			else{
				printf("Error type 7 at Line %d: Type mismatched for operands.\n", des->row);
				semantic_error++;
				return NULL;
			}
		}
		if(strcmp(des->sib->name,"LB")==0){
			//Exp->Exp LB Exp RB
			Type array=Exp(des);
			
			if(array==NULL)
				return NULL;
			if(array->kind!=ARRAY){
				printf("Error type 10 at Line %d: \"%s\" is not an array.\n", des->row, des->des->attr_val);
				semantic_error++;
				return NULL;
			}
			des = des->sib->sib;
			Type arrayNumber = Exp(des);
			if(arrayNumber==NULL)
				return NULL;
			if(arrayNumber->kind!=BASIC || arrayNumber->basic!=TYPE_INT){
				printf("Error type 12 at Line %d: \"%s\" is not an integer.\n", des->row , des->des->attr_val);
				semantic_error++;
				return NULL;
			}
			Type rtn = (Type)malloc(sizeof(struct Type_));
			memcpy(rtn, array->array.elem, sizeof(struct Type_));
			rtn->assign = BOTH;
			return rtn;
		}
		if(strcmp(des->sib->name,"DOT")==0){
			//Exp->Exp DOT ID
			Type structure = Exp(des);
			if(structure==NULL)
				return NULL;
			if(structure->kind!=STRUCTURE){
				printf("Error type 13 at Line %d: Illegal use of \".\".\n", des->row);
				semantic_error++;
				return NULL;
			}
			FieldList structDomain = structure->structure->domain;
			des = des->sib->sib;
			while(structDomain!=NULL){
				if(strcmp(structDomain->name, des->attr_val) ==0){
					Type rtn = (Type)malloc(sizeof(struct Type_));
					memcpy(rtn, structDomain->type, sizeof(struct Type_));
					rtn->assign = BOTH;
					return rtn;
				}
				structDomain = structDomain->tail;  
			}
			printf("Error type 14 at Line %d: Non-existent field \"%s\".\n", des->row, des->attr_val);
			semantic_error++;
			return NULL;
		}

		assert(0);
	}

	assert(0);
}

bool Args(TreeNode *n, FieldList param){
	assert(n);
	assert(strcmp(n->name,"Args")==0);
	
	TreeNode *des = n->des;
	if(param==NULL)
		//more params
		return FALSE;

	//Args -> Exp COMMA Args | Exp
	Type tmpParam = Exp(des);
	if(typeEqual(param->type, tmpParam)==TRUE){
		if(des->sib==NULL)
			if(param->tail==NULL)
				return TRUE;
			else
				//fewer params
				return FALSE;
		else
			return Args(des->sib->sib, param->tail);
	}
	else
		return FALSE;
}
